import numpy as np
import matplotlib.pyplot as plt
from scipy.io import loadmat

import pandas as pd
df = pd.read_csv('datasets_ML2/1809A_digit.csv')

x = df.iloc[:, 1:-1].values / 255
y = df.iloc[:, -1:].values

print("x, y: ", x.shape, y.shape)

X = np.insert(x, 0, 1, axis=1)
y = np.array([1 if label == 5 else 0 for label in y])
y = y.reshape((-1, 1))

m, n = X.shape

# data shufle
p = np.random.permutation(m)
X, y = X[p], y[p]

# data split
train_size = 0.7
devide = int(train_size * m)
X_train, X_test = np.split(X, [devide])
y_train, y_test = np.split(y, [devide])

# model
def model(theta, X):
    z = np.dot(X, theta)
    h = 1 / (1 + np.exp(-z))
    return h

# cost function
def computeCost(h, y):
    cost = - np.mean(y * np.log(h) + (1 - y) * np.log(1 - h))
    return cost

# gradient descend
def descendGradient(h, X, y):
    error = h - y
    d_theta = 1 / m * np.dot(X.T, error)
    return d_theta

# hyperparameters
iters = 3001
alpha = 0.1
def trainModel(theta, X, y):
    cost_his = []
    for step in range(iters):
        h = model(theta, X)
        cost_val = computeCost(h, y)
        d_theta = descendGradient(h, X, y)
        # parameter update
        theta = theta - alpha * d_theta
        if step % 100 == 0:
            print("step: ", step, "cost: ", cost_val)
            cost_his.append(np.squeeze(cost_val))
    return theta, cost_his

# main program
theta = np.random.uniform(-0.5, 0.5, [n, 1])
theta, cost_his = trainModel(theta, X_train, y_train)

plt.plot(cost_his[1:])
plt.show()

# accuracy
h = model(theta, X_test)
predict_y = np.where(h>0.5, 1, 0)
acc = np.mean(np.equal(predict_y, y_test))
print("accuracy: ", acc)

# compute confusion matrix
def computeConfusionMatrix(y, h):
    TP, FP, TN, FN = 0, 0, 0, 0
    for i in range(len(y)):
        if y[i] == 0 and h[i] == 0:
            TN += 1
        if y[i] == 0 and h[i] == 1:
            FP += 1
        if y[i] == 1 and h[i] == 0:
            FN += 1
        if y[i] == 1 and h[i] == 1:
           TP += 1
    return TN, FP, FN, TP

TN, FP, FN, TP = computeConfusionMatrix(y_test, predict_y)
confusion_mat = np.array([[TN, FP], [FN, TP]])
print("confusion_mat: \n", confusion_mat)

print("P: ", TP/(TP+FP))
print("R: ", TP/(TP+FN))
print("F1: ", TP/(TP+(FP+FN)/2))
print("accuracy: ", (TP+TN)/(TP+TN+FP+FN))

# confusion matrix
from sklearn.metrics import confusion_matrix, precision_score, recall_score, f1_score, accuracy_score

print("调库 confusion_mat: \n", confusion_matrix(y_test, predict_y))
print("调库 P: ", precision_score(y_test, predict_y))
print("调库 R: ", recall_score(y_test, predict_y))
print("调库 F1: ", f1_score(y_test, predict_y))
print("调库 accuracy: ", accuracy_score(y_test, predict_y))


from sklearn.metrics import classification_report
print(classification_report(y_test, predict_y))
